package io.gitee.caoxiaoyu97.messageconvert.client;

import io.gitee.caoxiaoyu97.messageconvert.client.codeconvert.CxyCloudAdapterInCodeConvert;
import io.gitee.caoxiaoyu97.messageconvert.client.codeconvert.CxyCloudAdapterInCodeConvertFactory;
import io.gitee.caoxiaoyu97.messageconvert.client.customer.CxyCloudAdapterConvertMetadata;
import io.gitee.caoxiaoyu97.messageconvert.client.customer.CxyCloudAdapterCustomerMetadata;
import io.gitee.caoxiaoyu97.messageconvert.client.customer.CxyCloudAdapterService;
import io.gitee.caoxiaoyu97.messageconvert.client.customer.CxyCloudAdapterServiceFactory;
import io.gitee.caoxiaoyu97.messageconvert.client.ext.CxyCloudAdapterConvertProcessor;
import io.gitee.caoxiaoyu97.messageconvert.client.ext.CxyCloudAdapterConvertProcessorAll;
import io.gitee.caoxiaoyu97.messageconvert.client.ext.CxyCloudAdapterConvertProcessorFactory;
import io.gitee.caoxiaoyu97.messageconvert.common.CxyCloudTempConfigurationUtil;
import io.gitee.caoxiaoyu97.messageconvert.exception.CxyException;
import io.gitee.caoxiaoyu97.util.findclass.FindClassUtil;
import io.gitee.caoxiaoyu97.util.StrFormatUtils;
import io.gitee.caoxiaoyu97.util.StreamUtil;
import lombok.extern.slf4j.Slf4j;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.Collectors;

/*********************************************************
 * 文件名称：CxyCloudAdapterClientUtil.java
 * 系统名称：cxycloud
 * 模块名称：io.gitee.caoxiaoyu97.messageconvert.client
 * 功能说明：适配器工具类，用来加载转换配置
 * 开发人员 @author：caoxy
 * 开发时间 @date：2023/7/19 13:47
 * 修改记录：程序版本  修改日期  修改人员  修改单号  修改说明
 *********************************************************/
@Slf4j
public class CxyCloudAdapterClientUtil {

    private static Map<String, Map<String, CxyCloudAdapterCustomerMetadata>> groupCustomersMap = new LinkedHashMap<>();
    private static Map<String, CxyCloudAdapterCustomerMetadata> cacheCustomersMap = new HashMap<>();
    private static CxyCloudAdapterServiceFactory cxyCloudAdapterServiceFactory;

    private static CxyCloudAdapterInCodeConvertFactory cxyCloudAdapterInCodeConvertFactory;
    private static CxyCloudAdapterConvertProcessorFactory cxyCloudAdapterConvertProcessorFactory;


    /**
     * 初始化多个模板路径，实际调用从前往后进行模板查找
     *
     * @param groups
     */
    public static void init(String... groups) {
        initCxyCloudAdapterServiceFactory();
        initCxyCloudAdapterInCodeConvertFactory();
        initCxyCloudAdapterConvertProcessorFactory();
        cacheClientXml(groups);
        cacheService();
    }

    private static void initCxyCloudAdapterInCodeConvertFactory() {
        ServiceLoader<CxyCloudAdapterInCodeConvertFactory> serviceFactories =
                ServiceLoader.load(CxyCloudAdapterInCodeConvertFactory.class);
        Iterator<CxyCloudAdapterInCodeConvertFactory> iterator = serviceFactories.iterator();
        if (iterator.hasNext()) {
            cxyCloudAdapterInCodeConvertFactory = iterator.next();
        } else {
            throw new CxyException("9900",
                    "未找到CxyCloudAdapterInCodeConvertFactory实现，请配置src/main/resources/META-INF/services/" +
                            CxyCloudAdapterInCodeConvertFactory.class.getName());
        }
    }

    private static void initCxyCloudAdapterConvertProcessorFactory() {
        ServiceLoader<CxyCloudAdapterConvertProcessorFactory> processorFactories =
                ServiceLoader.load(CxyCloudAdapterConvertProcessorFactory.class);
        Iterator<CxyCloudAdapterConvertProcessorFactory> iterator = processorFactories.iterator();
        if (iterator.hasNext()) {
            cxyCloudAdapterConvertProcessorFactory = iterator.next();
        } else {
            throw new CxyException("9900",
                    "未找到CxyCloudAdapterConvertProcessorFactory实现，请配置src/main/resources/META-INF/services/" +
                            CxyCloudAdapterConvertProcessorFactory.class.getName());
        }
    }

    /**
     * 缓存服务
     */
    private static void cacheService() {
        groupCustomersMap.forEach((k, v)->{
            v.forEach((k1,v1)->{
                cacheCustomersMap.putIfAbsent(k1,v1);
            });
        });
    }

    /**
     * 增加处理器
     * @param cxyCloudAdapterClientMetadata
     */
    private static void addProcessor(CxyCloudAdapterClientMetadata cxyCloudAdapterClientMetadata) {
        String extPackage = cxyCloudAdapterClientMetadata.getExtPackage();
        if (extPackage == null || "".equals(extPackage.trim())){
            return;
        }
        Map<String, CxyCloudAdapterConvertMetadata> convertMetadataMap =
                cxyCloudAdapterClientMetadata.getCustomerMetadataMap().values().stream()
                        .map(CxyCloudAdapterCustomerMetadata::getConvertMetadataMap)
                        .reduce(new HashMap<>(),(a,b)->{a.putAll(b);return a;});

        Set<Class<? extends CxyCloudAdapterConvertProcessor>> classByParentType =
                FindClassUtil.getClassByParentType(CxyCloudAdapterConvertProcessor.class, extPackage);
        Map<String, List<CxyCloudAdapterConvertProcessor>> adapterConvertProcessorMap =
                classByParentType.stream()
                        .filter(cla -> !cla.isInterface())
                        .map(processorClass -> cxyCloudAdapterConvertProcessorFactory.getCxyCloudAdapterConvertProcessor(processorClass))
                        .filter(Objects::nonNull)
                        .collect(Collectors.groupingBy(CxyCloudAdapterConvertProcessor::getOutCode));
        convertMetadataMap.forEach((k,v)->{
            v.getProcessorList().addAll(adapterConvertProcessorMap.getOrDefault(CxyCloudAdapterConvertProcessorAll.allCode, new ArrayList<>()));
            v.getProcessorList().addAll(adapterConvertProcessorMap.getOrDefault(k,new ArrayList<>()));
        });
    }

    private static void initCxyCloudAdapterServiceFactory() {
        ServiceLoader<CxyCloudAdapterServiceFactory> serviceFactories =
                ServiceLoader.load(CxyCloudAdapterServiceFactory.class);
        Iterator<CxyCloudAdapterServiceFactory> iterator = serviceFactories.iterator();
        if (iterator.hasNext()) {
            cxyCloudAdapterServiceFactory = iterator.next();
        } else {
            throw new CxyException("9900",
                    "未找到CxyCloudAdapterServiceFactory实现，请配置src/main/resources/META-INF/services/" +
                            CxyCloudAdapterServiceFactory.class.getName());
        }
    }

    private static void cacheClientXml(String... groups) {
        for (String group : groups) {
            /**
             * 格式化处理开始
             */
            if (group.startsWith("/")) {
                group = group.substring(1);
            }
            if (group.endsWith("/")) {
                group = group.substring(0,group.length()-1);
            }
            /**
             * 格式化处理结束，处理完的路径为xxxxx/xxxxx
             */
            Enumeration<URL> resources;
            try {
                resources = CxyCloudAdapterClientUtil.class.getClassLoader().getResources(group);
            } catch (IOException e) {
                String msg = group + " 加载失败";
                log.error(msg, e);
                throw new CxyException("9900",msg,e);
            }
            resources.asIterator().forEachRemaining(groupUrl -> {
                CxyCloudAdapterClientMetadata cxyCloudAdapterClientMetadata = loadCustomersByGroupUrl(groupUrl);
                addTempData(groupUrl, cxyCloudAdapterClientMetadata);
                CxyCloudTempConfigurationUtil.addConfiguration(groupUrl.toString(),
                        cxyCloudAdapterClientMetadata.getCustomerMetadataMap().values().stream()
                                .map(CxyCloudAdapterCustomerMetadata::getConvertMetadataMap)
                                .map(Map::values)
                                .map(convertMetadataList-> convertMetadataList.stream().toList())
                                .reduce(new ArrayList<>(),(a,b)->{a.addAll(b);return a;}));
                addProcessor(cxyCloudAdapterClientMetadata);
                groupCustomersMap.put(groupUrl.toString(), cxyCloudAdapterClientMetadata.getCustomerMetadataMap());
            });
        }
    }

    /**
     * 加载server.xml
     * @param groupUrl
     * @return
     */
    private static CxyCloudAdapterClientMetadata loadCustomersByGroupUrl(URL groupUrl){
        InputStream inputStream;
        try {
            inputStream = new URL(groupUrl + "/" + "client.xml").openStream();
        } catch (IOException e) {
            String msg = groupUrl.toString() + " client.xml 流加载失败";
            log.error(msg, e);
            throw new CxyException("9900",msg,e);
        }
        try {
            return loadCustomers(inputStream);
        } catch (Exception e) {
            String msg = groupUrl.toString() + " client.xml 处理失败";
            log.error(msg, e);
            throw new CxyException("9900",msg,e);
        }
    }

    /**
     * 添加模板信息
     * @param groupUrl
     * @param clientMetadata
     */
    private static void addTempData(URL groupUrl,CxyCloudAdapterClientMetadata clientMetadata) {
        clientMetadata.getCustomerMetadataMap().forEach((k, customerMetadata) -> {
            for (CxyCloudAdapterConvertMetadata convertMetadata : customerMetadata.getConvertMetadataMap().values()) {
                convertMetadata.setTempGroup(groupUrl.toString());
                String outCode = convertMetadata.getOutCode();
                convertMetadata.setReqTempName(StrFormatUtils.formatMessage("req/req.{}.ftl", outCode));
                convertMetadata.setRespTempName(StrFormatUtils.formatMessage("resp/resp.{}.ftl", outCode));
                convertMetadata.setMockTempName(StrFormatUtils.formatMessage("mock/mock.{}.ftl", outCode));
                convertMetadata.setReqTemp(getStrByUrl(groupUrl, convertMetadata.getReqTempName()));
                convertMetadata.setRespTemp(getStrByUrl(groupUrl, convertMetadata.getRespTempName()));
                try {
                    convertMetadata.setMockTemp(getStrByUrl(groupUrl, convertMetadata.getMockTempName()));
                }catch (Exception e){
                    if (convertMetadata.isMock()){
                        throw e;
                    }else{
                        convertMetadata.setMockTemp("");
                    }
                }
            }
        });
    }

    private static String getStrByUrl(URL groupUrl, String path) {
        String result;
        try {
            InputStream inputStream = new URL(groupUrl + "/" + path).openStream();
            result = StreamUtil.readBytesAll(inputStream, StandardCharsets.UTF_8);
        } catch (IOException e) {
            String msg = groupUrl.toString() + "/" + path + " 流加载失败";
            throw new CxyException("9900",msg,e);
        }
        return result;
    }

    private static CxyCloudAdapterClientMetadata loadCustomers(InputStream inputStream) throws Exception {
        Map<String, CxyCloudAdapterCustomerMetadata> customerMetadataHashMap = new HashMap<>();

        String extPackage = null;
        Document doc = loadXmlFromInputStream(inputStream);
        Node extPackageNode = doc.getElementsByTagName("extPackage").item(0);
        if (extPackageNode != null) {
            extPackage = ((Element) extPackageNode).getAttribute("name");
        }
        NodeList nodeList = doc.getElementsByTagName("customer");
        for (int i = 0; i < nodeList.getLength(); i++) {
            Element element = (Element) nodeList.item(i);
            String inCode = element.getAttribute("inCode");
            String inCodeConvertBeanName = element.getAttribute("inCodeConvertBeanName");
            NodeList convertNodes = element.getElementsByTagName("convert");
            Map<String,CxyCloudAdapterConvertMetadata> cxyCloudAdapterConvertMetadataMap = getCxyCloudAdapterConvertMetadataList(convertNodes);
            CxyCloudAdapterCustomerMetadata customerMetadata = new CxyCloudAdapterCustomerMetadata(inCode, inCodeConvertBeanName, cxyCloudAdapterConvertMetadataMap);
            customerMetadataHashMap.put(inCode, customerMetadata);
        }
        return new CxyCloudAdapterClientMetadata(extPackage,customerMetadataHashMap);
    }

    private static Map<String,CxyCloudAdapterConvertMetadata> getCxyCloudAdapterConvertMetadataList(NodeList convertNodes) {
        List<CxyCloudAdapterConvertMetadata> cxyCloudAdapterConvertMetadataList = new ArrayList<>();
        for (int j = 0; j < convertNodes.getLength(); j++) {
            Element convert = (Element) convertNodes.item(j);
            String outCode = convert.getAttribute("outCode");
            String mock = "".equals(convert.getAttribute("mock")) ? "false" : convert.getAttribute("mock");
            String outServiceBeanName = convert.getAttribute("outServiceBeanName");
            cxyCloudAdapterConvertMetadataList.add(new CxyCloudAdapterConvertMetadata(outCode,outServiceBeanName, Boolean.parseBoolean(mock)));
        }
        return cxyCloudAdapterConvertMetadataList.stream().collect(Collectors.toMap(CxyCloudAdapterConvertMetadata::getOutCode,obj->obj));
    }

    private static Document loadXmlFromInputStream(InputStream inputStream) throws Exception {
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        dbFactory.setIgnoringElementContentWhitespace(true);
        dbFactory.setValidating(true);
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
        dBuilder.setEntityResolver(((publicId, systemId) -> {
            if (publicId.equals("cxycloud-messageconvert-server.dtd")){
                return new InputSource(CxyCloudAdapterClientUtil.class.getClassLoader().getResourceAsStream("io/gitee/caoxiaoyu97/dtd/server.dtd"));
            }
            if (publicId.equals("cxycloud-messageconvert-client.dtd")){
                return new InputSource(CxyCloudAdapterClientUtil.class.getClassLoader().getResourceAsStream("io/gitee/caoxiaoyu97/dtd/client.dtd"));
            }
            return null;
        }));
        Document doc = dBuilder.parse(inputStream);
        doc.getDocumentElement().normalize();
        return doc;
    }

    public static CxyCloudAdapterService getAdapterService(String outServiceBeanName) {
        return cxyCloudAdapterServiceFactory.getCxyCloudAdapterService(outServiceBeanName);
    }
    public static CxyCloudAdapterInCodeConvert getAdapterCodeConvert(String inCodeConvertBeanName) {
        return cxyCloudAdapterInCodeConvertFactory.getCxyCloudAdapterCodeConvert(inCodeConvertBeanName);
    }

    public static CxyCloudAdapterCustomerMetadata getAdapterCustomerMetadata(String inCode) {
        CxyCloudAdapterCustomerMetadata gateWayServerMetadata = cacheCustomersMap.get(inCode);
        if (gateWayServerMetadata == null) {
            throw new CxyException("9900", "未找到此服务");
        }
        return gateWayServerMetadata;
    }
    public static Map<String, CxyCloudAdapterCustomerMetadata> getAllCustomer(){
        return Collections.unmodifiableMap(cacheCustomersMap);
    }
}
